// N1UnicodeString.cpp: implementation of the N1UnicodeString class.
//
//////////////////////////////////////////////////////////////////////

#include "N1UnicodeString.h"
#include "N1Exception.h"
#include "N1Math.h"
#include <string.h>

static wchar_t* _gEmptyString = L"";

N1UnicodeString::N1UnicodeString() : m_pStr(_gEmptyString), m_nRealCount(0)
{
}

N1UnicodeString::N1UnicodeString(const N1StringChar* str)  
 : m_pStr((N1StringChar*)str)
{  
    m_nRealCount = 0;
}

N1UnicodeString::N1UnicodeString(const N1UnicodeString &str)
{  
    m_nRealCount = str.m_nRealCount;
    if(m_nRealCount==0)
    {
        m_pStr = str.m_pStr;
    } 
    else 
    {
        m_pStr = alloc( m_nRealCount );
        wcsncpy(m_pStr, str.m_pStr, m_nRealCount);
    }
}

N1UnicodeString::~N1UnicodeString() 
{
    if(m_nRealCount)
    {
        N1Free(m_pStr);
    }
}

N1UnicodeString::N1StringChar* N1UnicodeString::alloc(N1UInt32 count) const
{
    return (N1StringChar*)N1Malloc( count * sizeof(N1StringChar) );
}

N1NAME_DEF(N1UnicodeString)


N1UInt32 N1UnicodeString::length() const 
{
    return wcslen(m_pStr);
}

void N1UnicodeString::setLength(N1UInt32 len)
{
    N1StringChar *pStr = alloc(len);
    memcpy(pStr, m_pStr, sizeof(N1StringChar) * 
           N1Min(len, wcslen(m_pStr)+1));

    if(m_nRealCount)
    {
        N1Free(m_pStr);
    }
    m_pStr = pStr;
    m_pStr[len - 1] = 0;
    m_nRealCount = len;
}

N1UnicodeString::N1StringChar N1UnicodeString::at(N1UInt32 index) const 
{
    return m_pStr[index];
}

bool N1UnicodeString::operator==(const N1UnicodeString& str) const 
{
    return wcscmp(m_pStr, str.m_pStr)==0;
}

bool N1UnicodeString::operator==(const N1StringChar* str) const 
{
    if(!str)
    {
        if (*m_pStr == 0)
        {
            return true;
        }
        return false;
    }
    return wcscmp(m_pStr, str)==0;
}

void N1UnicodeString::formatv(const N1StringChar* format, va_list args)
{
    int size = vsnwprintf(NULL, 0, format, args);
    if(size>0)
    {
        setLength(size + 1);
        vsnwprintf(m_pStr, size + 1, format, args);
    }
    else 
    {
        N1Exception::Throw("N1UnicodeString::formatv: n1vsnprintf returned %d", size);
    }
}

void N1UnicodeString::format(const N1StringChar* format, ...)
{
    va_list argList;
    va_start( argList, format );

    formatv(format, argList);

    va_end(argList);
}

const N1UnicodeString& N1UnicodeString::operator+=(N1StringChar *str)
{
    setLength(length() + wcslen(str) + 1);
    wcscat(m_pStr, str);
    return *this;
}

void N1UnicodeString::releaseBuffer()
{
    uint32 len = wcslen(m_pStr);
    setLength(len + 1);
}

N1String* N1UnicodeString::UTF8()
{
    const int SixBits = 0x3F;
    const int FourBits = 0xF;

    N1String *ret = (new N1String())->autorelease<N1String>();

    const wchar_t *ptr = str();
    wchar_t c;

    while((c = *ptr++))
    {
        if(c<=0x007F)
        {
            *ret += (char)c;
        } 
        else if(c<=0x07FF)
        {
            *ret += (char)(0xC0 | ((c>>6)&SixBits));
            *ret += (char)(0x80 | (c&SixBits));
        } 
        else 
#ifndef WINDOWS
        if(c<=0xFFFF)
#endif
        {
            *ret += (char)(0xE0 | ((c>>12)&FourBits));
            *ret += (char)(0x80 | ((c>>6)&SixBits));
            *ret += (char)(0x80 | (c&SixBits));
        }
#ifndef WINDOWS
        else if(c<0x001FFFFF)
        {
            N1NonImplemented;
        }
        else if(c<0x03FFFFFF)
        {
            N1NonImplemented;
        }
        else /* if(c<0x7FFFFFFF) */
        {
            N1NonImplemented;
        }
#endif
    }
    return ret;
}
 
#ifdef N1LIB_SELFTEST

N1TEST(N1UnicodeStringTestConstructors)
{
    N1UnicodeString u1;
    N1UnicodeString u2(L"Unicode constructon");
}

N1TEST(N1UnicodeStringTestOperatorCharPtr)
{
    const wchar_t str2[] = L"Unicode string";

    N1UnicodeString u1(str2);

    N1CHECK(wcscmp(u1.str(), str2)==0, 2);
}

N1TEST(N1UnicodeStringTestLength)
{
    const wchar_t str2[] = L"12345678";

    N1UnicodeString u1(str2);
    N1UnicodeString u2;

    N1CHECK(u1.length()==8, 10);
    N1CHECK(u2.length()==0, 11);
}

N1TEST(N1UnicodeStringTestOperatorBrackets)
{
    const wchar_t str2[] = L"Unicode string";

    N1UnicodeString u1(str2);

    uint32 i;

    for(i=0;i<u1.length();i++)
    {
	    N1CHECK(u1[i]==str2[i], i + 21);
    }
}

N1TEST(N1UnicodeStringCopyConstructors)
{

    N1UnicodeString u2(L"Unicode constructon");
    N1UnicodeString u3(u2);

    uint32 i;

    for(i=0;i<u2.length();i++)
    {
	    N1CHECK(u2[i]==u3[i], i + 21);
    }
}

N1TEST(N1UnicodeStringFormat)
{
    N1UnicodeString u2(L"Test message 1 2 msg");
    N1UnicodeString u3;

    u3.format(L"Test message %d %u %s", 1, 2, L"msg");

    N1CHECK(u3==u2, 2);
}

#endif //N1LIB_SELFTEST
